<?php

class DAO_MySql extends DAO {

    /**
     * add
     *
     * @param string $table 	数据表名称
     * @param array $data		字段、值的关联数组
     * @param array $requiredFields 必须的字段
     * @param array $allowedFields  允许的字段
     * @throws InvalidArgumentException
     * @throws DAO_Exception
     * @return mixed 成功返回新记录的id数
     */
    public static function add($table, $data, $requiredFields, $allowedFields, $shardKey = array()) {

        if (!is_array($requiredFields)) {
            throw new InvalidArgumentException('Praram $RequiredFields Cannt Be Empty!');
        }
        if (!$allowedFields || !is_array($allowedFields)) {
            throw new InvalidArgumentException('Praram $allowedFields Cannt Be Empty!');
        }

        $fields = array_keys($data);
        $result = array_diff($fields, $allowedFields);
        if ($result) {
            throw new InvalidArgumentException("Invalid Field(" . join(',', $result) . ")");
        }
        $result = array_diff($requiredFields, $fields);
        if ($result) {
            throw new InvalidArgumentException(join(',', $result) . " is Required.");
        }

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $db->save($data);
            //Benchmark::addGroupMark('MySql');

            $lastInsertId = $db->lastInsertId();
            // 字符串主键不支持 lastInsertId
            if ($lastInsertId === '0') {
                // 需要支持返回主键
                return true;
            }
            return $lastInsertId;
        } catch (DB_Exception $e) {
            switch ($e->getCode()) {
                case '23000':
                    throw new DAO_DuplicateException($table . $e->getMessage(), $e->getCode());
                    break;
                default:
                    throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
            }
        }
        return false;
    }

    /**
     * replace
     *
     * @param string $table 	数据表名称
     * @param array $data		字段、值的关联数组
     * @param array $requiredFields 必须的字段
     * @param array $allowedFields  允许的字段
     * @throws InvalidArgumentException
     * @throws DAO_Exception
     * @return mixed 成功返回新记录的id数
     */
    public static function replace($table, $data, $requiredFields, $allowedFields, $shardKey = array()) {

        if (!is_array($requiredFields)) {
            throw new InvalidArgumentException('Praram $RequiredFields Cannt Be Empty!');
        }
        if (!$allowedFields || !is_array($allowedFields)) {
            throw new InvalidArgumentException('Praram $allowedFields Cannt Be Empty!');
        }

        $fields = array_keys($data);
        $result = array_diff($fields, $allowedFields);
        if ($result) {
            throw new InvalidArgumentException("Invalid Field(" . join(',', $result) . ")");
        }
        $result = array_diff($requiredFields, $fields);
        if ($result) {
            throw new InvalidArgumentException(join(',', $result) . " is Required.");
        }

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $db->replace($data);
            //Benchmark::addGroupMark('MySql');

            $lastInsertId = $db->lastInsertId();
            // 字符串主键不支持 lastInsertId
            if ($lastInsertId === '0') {
                // 需要支持返回主键
                return true;
            }
            return $lastInsertId;
        } catch (DB_Exception $e) {
            switch ($e->getCode()) {
                case '23000':
                    throw new DAO_DuplicateException($table . $e->getMessage(), $e->getCode());
                    break;
                default:
                    throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
            }
        }
        return false;
    }

    /**
     * update 根据主键更新记录
     *
     * @param string $table	 表名
     * @param array $data	   字段、值的关联数组
     * @param string $pk		主键
     * @param integer $pkValue  主键的值
     * @param array $allowedFields 允许更新的字段
     * @throws InvalidArgumentException
     * @throws DAO_Exception
     * @return mixed 成功返回true
     */
    public static function update($table, $data, $pk, $pkValue, $allowedFields, $shardKey = array()) {

        if (!$allowedFields || !is_array($allowedFields)) {
            throw new InvalidArgumentException('Param $allowedFields Cannt Be Empty');
        }

        $fields = array_keys($data);
        $result = array_diff($fields, $allowedFields);
        if ($result) {
            throw new InvalidArgumentException("Invalid Field(" . join(',', $result) . ")");
        }

        $conditions = "$pk = ?";
        $params = array($pkValue);

        return self::updateBycondition($table, $data, $conditions, $params, $allowedFields, $shardKey);
    }

    /**
     * updateBycondition
     * 根据某个字段更新一条或多条记录
     *
     * @param  string $table
     * @param  array $data
     * @param  array $conditions
     * @param  array $params
     * @param  array $shardKey
     * @return void
     */
    public static function updateBycondition($table, $data, $conditions, $params, $allowedFields, $shardKey = array()) {

        if (!$allowedFields || !is_array($allowedFields)) {
            throw new InvalidArgumentException('Param $allowedFields Cannt Be Empty');
        }

        $fields = array_keys($data);
        $result = array_diff($fields, $allowedFields);
        if ($result) {
            throw new InvalidArgumentException("Invalid Field(" . join(',', $result) . ")");
        }

        try {

            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $ret = $db->save($data, $conditions, $params);
            //Benchmark::addGroupMark('MySql');
            return $ret;
        } catch (PDOException $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }
    }

    /**
     * remove
     * 根据主键删除单条记录
     *
     * @param string $table 表名
     * @param string $pk	主键
     * @param mixed $pkValue 值
     * @throws DAO_Exception
     * @return boolean
     */
    public static function remove($table, $pk, $pkValue, $shardKey = array()) {

        try {

            $conditions = "$pk = ?";
            $params = array($pkValue);

            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $ret = $db->remove($conditions, $params);
            //Benchmark::addGroupMark('MySql');
            return $ret;
        } catch (PDOException $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }

        return false;
    }

    /**
     * removeBycondition
     * 根据条件删除记录
     *
     * @param string $table 表名
     * @param string $conditions 条件
     * @param array $params 值
     * @throws DAO_Exception
     * @return boolean
     */
    public static function removeBycondition($table, $conditions, $params, $shardKey = array()) {

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $ret = $db->remove($conditions, $params);
            //Benchmark::addGroupMark('MySql');
            return $ret;
        } catch (PDOException $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }

        return false;
    }

    /**
     * execBySql
     * @param string $sql SQL语句
     * @throws DAO_Exception
     * @return boolean
     */
    public static function execBySql($table, $sql, $params = array(), $shardKey = array()) {

        if (!$sql || !is_array($params)) {
            throw new InvalidArgumentException('Param $sql Cannt By Empty');
        }

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $result = $db->exec($sql, $params);
            //Benchmark::addGroupMark('MySql');
        } catch (PDOException $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }

        return $result;
    }

    /**
     * get
     * 根据主键获取一条记录
     *
     * @param  string $table
     * @param  string $key
     * @param  mixed $value
     * @param  array $columns
     * @param  array $shardKey
     * @return void
     */
    public static function get($table, $key, $value, $columns = array('*'), $shardKey = array()) {

        $conditions = "$key = ?";
        $params = array($value);

        return self::find($table, $conditions, $params, $columns, $shardKey);
    }

    /**
     * findByIds
     * 根据id获取多条记录
     *
     * @param  mixed $table
     * @param  mixed $key
     * @param  mixed $ids
     * @param  string $columns
     * @param array $shardKey
     * @return void
     */
    public static function findByIds($table, $key, $ids, $pageOptions = array(), $columns = array('*'),
                                     $orderField = null, $orderType = null, $shardKey = array()) {

        $params = array();
        $conditions = $key . ' IN (' . self::buildInSQLClause($ids, $params) . ')';

        return self::findAll($table, $conditions, $params, $pageOptions, $columns, $orderField, $orderType, $shardKey);
    }

    /**
     * findAll
     * 取得多条记录
     *
     * @param string $table
     * @param array $conditions
     * @param array $params
     * @param array $pageOptions
     * @param array $columns
     * @param string $order
     *   fieldName1 => [ASC|DESC]
     *   fieldName2 => [ASC|DESC]
     * @param array $shardKey
     * @return array
     */
    public static function findAll($table, $conditions = null, $params = array(), $pageOptions = array(),
                                   $columns = array('*'), $order = array(), $shardKey = array()) {

        if ($params && !is_array($params)) {
            throw new InvalidArgumentException('$params must be an array');
        }

        if ($columns && !is_array($columns)) {
            throw new InvalidArgumentException('$columns must be an array');
        }

        if ($pageOptions && !is_array($pageOptions)) {
            throw new InvalidArgumentException('$pageOptions must be an array');
        }

        if ($order && !is_array($order)) {
            throw new InvalidArgumentException('$order must be an array');
        }

        if ($shardKey && !is_array($shardKey)) {
            throw new InvalidArgumentException('$shardKey must be an array');
        }

        try {

            if (!$pageOptions['perPage']) {
                $start = $limit = 0;
            } else {
                $start = $pageOptions['start'];
                $limit = $pageOptions['perPage'];
            }
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $data = $db->findAll($conditions, $params, $columns, $start, $limit, $order);
            //Benchmark::addGroupMark('MySql');
        } catch (Exception $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }

        return $data;
    }

    /**
     * 按照范围获取mysql 数据
     *
     * @param $table
     * @param $start
     * @param $limit
     * @param $order
     * @param $conditions
     * @param $params
     * @param $shardKey
     *
     * @returns
     */
    public function findRange($table, $start = 0, $limit = 0, $columns = array('*'), $order = array(), $conditions = null, $params = array(), $shardKey = array()) {

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $data = $db->findAll($conditions, $params, $columns, $start, $limit, $order);
            //Benchmark::addGroupMark('MySql');
        } catch (Exception $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }

        return $data;
    }


    /**
     * find
     * 获取一条记录
     *
     * @param  mixed $table
     * @param  mixed $conditions
     * @param  mixed $params
     * @param  string $columns
     * @param  array $shardKey
     * @return void
     */
    public static function find($table, $conditions = null, $params = array(), $columns = array('*'), $shardKey = array()) {

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $data = $db->find($conditions, $params, $columns);
            //Benchmark::addGroupMark('MySql');
        } catch (Exception $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }

        return $data;
    }

    /**
     * findBySql
     *
     * @param  mixed $table
     * @param  mixed $sql
     * @param  array $params
     * @param  array $shardKey
     * @return array
     */
    public static function findBySql($table, $sql, $params = array(), $shardKey = array()) {

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $data = $db->findBySql($sql, $params);
            //Benchmark::addGroupMark('MySql');
        } catch (Exception $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }

        return $data;
    }

    /**
     * count
     * 计算总数
     *
     * @param  mixed $table
     * @param  mixed $conditions
     * @param  array $params
     * @param  array $shardKey
     * @return void
     */
    public static function count($table, $conditions = null, $params = array(), $shardKey = array()) {

        if ($params && !is_array($params)) {
            throw new InvalidArgumentException('$params must is an array');
        }

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $num = $db->count($conditions, $params);
            //Benchmark::addGroupMark('MySql');
        } catch (Exception $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }

        return intval($num);
    }

    /**
     * xcount
     * count方法别名
     *
     * @param  mixed $table
     * @param  mixed $conditions
     * @param  array $params
     * @param  array $shardKey
     * @return void
     */
    public static function xcount($table, $conditions = null, $params = array(), $shardKey = array()) {

        return self::count($table, $conditions, $params, $shardKey);
    }

    /**
     * buildInSQLClause
     * 根据数据($data)返回, SQL IN 子句
     *
     * @param  mixed $data
     * @param  mixed $sqlParams
     * @throws InvalidArgumentException
     * @return string
     */
    public static function buildInSQLClause($data, &$sqlParams) {

        if (!is_array($data)) {
            throw new InvalidArgumentException('参数必须是查询数组');
        }

        $params = array();
        foreach ($data as $v) {
            $params[] = '?';
            $sqlParams[] = $v;
        }

        return join(',', $params);
    }

    /**
     * incr
     * 给一个字段增加一个单位值
     *
     * @param string $table 表名
     * @param string $field 字段名
     * @param string $conditions 条件
     * @param array $params 参数
     * @param integer $unit
     * @return boolean
     */
    public static function incr($table, $field, $conditions = null, $params = array(),  $unit = 1, $shardKey = array()) {

        if ($params && !is_array($params)) {
            throw new InvalidArgumentException('$params must is an array');
        }

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $ret = $db->incr($field, $conditions, $params, $unit);
            //Benchmark::addGroupMark('MySql');
            return $ret;
        } catch (Exception $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }
    }

    /**
     * decr
     * 给一个字段减少一个单位值
     *
     * @param string $table 表名
     * @param string $field 字段名
     * @param string $conditions 条件
     * @param array $params 参数
     * @param integer $unit
     * @return boolean
     */
    public static function decr($table, $field, $conditions = null, $params = array(),  $unit = 1, $shardKey = array()) {

        if ($params && !is_array($params)) {
            throw new InvalidArgumentException('$params must is an array');
        }

        try {
            //Benchmark::addGroupMark('MySql');
            $db = new DB_Table($table, $shardKey);
            $ret = $db->decr($field, $conditions, $params, $unit);
            //Benchmark::addGroupMark('MySql');
            return $ret;
        } catch (Exception $e) {
            throw new DAO_Exception($table . $e->getMessage(), $e->getCode());
        }
    }
}
